﻿// Partial Copyright (c) LogoUI Software Solutions LTD
// Autor: Vladislav Spivak
// This source file is the part of LogoFX Framework http://logofx.codeplex.com
// See accompanying licences and credits.

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Diagnostics;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Windows;
using System.Windows.Resources;
using System.Xml;

namespace LogoFX.Composition
{
    public static class MefExtensions
    {
#if SILVERLIGHT
        public static void LoadSerial(this IEnumerable<Uri> uris, Action<IEnumerable<ComposablePartCatalog>, bool> loadAction,bool stopOnError = false,bool cacheXaps = false)
        {
            Dictionary<string, ComposablePartCatalog> loadedCatalogs = new Dictionary<string, ComposablePartCatalog>();
            IEnumerator<Uri> enumerator = uris.GetEnumerator();
            bool wasError = false;
            Action worker = null;
            worker = () =>
            {
                while (true)
                {
                    if (enumerator.MoveNext())
                    {
                        ComposablePartCatalog catalogExisting;
                        string uri = enumerator.Current.ToString();
                        if (loadedCatalogs.TryGetValue(uri, out catalogExisting))
                            continue;
                        if (Application.Current.IsRunningOutOfBrowser)
                        {
                            using (var isoStorage = IsolatedStorageFile.GetUserStoreForApplication())
                            {
                                if (isoStorage.FileExists(uri))
                                {
                                    Stream moduleStream = isoStorage.OpenFile(uri,FileMode.Open);
                                    loadedCatalogs[uri] =
                                        new AggregateCatalog(LoadPackagedAssemblies(moduleStream).Select(a => (ComposablePartCatalog) new AssemblyCatalog(a)));
                                    continue;
                                }
                            }
                        }


                        WebClient webClient = new WebClient();

                        webClient.OpenReadCompleted += (s, e) =>
                        {
                            
                            if (e.Error == null)
                            {
                                //loadedCatalogs[uri] = catalog;
                                if(cacheXaps)
                                {
                                    using (var isoStorage = IsolatedStorageFile.GetUserStoreForApplication())
                                    {
                                        //if(isoStorage.Quota < 50000000)
                                        //{
                                        //    increaseStorageSpaceAction(
                                        //        (a) =>
                                        //            {
                                        //                if (a)
                                        //                {
                                        //                    try
                                        //                    {
                                        //                        isoStorage.IncreaseQuotaTo(50000000);
                                        //                        Debug.WriteLine("Storage increased to be 50Mb");
                                        //                    }
                                        //                    catch (Exception)
                                        //                    {
                                        //                        Debug.WriteLine("Failing to increase storage");                                                                
                                        //                    }
                                        //                }
                                        //            });
                                        //}
                                        IsolatedStorageFileStream stream = isoStorage.CreateFile(uri);
                                        //if (isoStorage.AvailableFreeSpace < e.Result.Length)                                            
                                        //    isoStorage.IncreaseQuotaTo(isoStorage.Quota + e.Result.Length);
                                        WriteModuleFile(stream, e.Result);
                                    }                                    
                                }
                                loadedCatalogs[uri] =
                                    new AggregateCatalog(LoadPackagedAssemblies(e.Result).Select(a => (ComposablePartCatalog)new AssemblyCatalog(a)));

                                worker();
                            }
                            else
                            {
                                wasError = true;
                                if (!stopOnError)
                                {
                                    worker();
                                }
                            }
                        };
                        webClient.OpenReadAsync(enumerator.Current);
                    }
                    else
                    {
                        loadAction(loadedCatalogs.Values, wasError);
                    }
                    break;
                }
            };

            //
            worker();
            //
        }
        public static void WriteModuleFile(IsolatedStorageFileStream isoStorageStream, Stream moduleStream)
        {
            int size = 4096;
            byte[] bytes = new byte[4096];
            int numBytes;
            while ((numBytes = moduleStream.Read(bytes, 0, size)) > 0)
                isoStorageStream.Write(bytes, 0, numBytes);
        }

        public static IEnumerable<Assembly> LoadPackagedAssemblies(Stream packageStream)
        {
            List<Assembly> list = new List<Assembly>();
            StreamResourceInfo streamResourceInfo = new StreamResourceInfo(packageStream, (string)null);
            foreach (AssemblyPart assemblyPart in GetDeploymentParts(streamResourceInfo))
            {
                StreamResourceInfo resourceStream = Application.GetResourceStream(streamResourceInfo, new Uri(assemblyPart.Source, UriKind.Relative));
                list.Add(assemblyPart.Load(resourceStream.Stream));
            }
            packageStream.Close();
            return (IEnumerable<Assembly>)list;
        }

        private static IEnumerable<AssemblyPart> GetDeploymentParts(StreamResourceInfo xapStreamInfo)
        {
            Uri uriResource = new Uri("AppManifest.xaml", UriKind.Relative);
            StreamResourceInfo resourceStream = Application.GetResourceStream(xapStreamInfo, uriResource);
            List<AssemblyPart> list = new List<AssemblyPart>();
            if (resourceStream != null)
            {
                using (XmlReader xmlReader = XmlReader.Create(resourceStream.Stream))
                {
                    if (xmlReader.ReadToFollowing("AssemblyPart"))
                    {
                        do
                        {
                            string attribute = xmlReader.GetAttribute("Source");
                            if (attribute != null)
                                list.Add(new AssemblyPart()
                                {
                                    Source = attribute
                                });
                        }
                        while (xmlReader.ReadToNextSibling("AssemblyPart"));
                    }
                }
            }
            return (IEnumerable<AssemblyPart>)list;
        }

#endif
    }
}
